#include "pool.h"
#include <cassert>
#include <iostream>
#include <cstring>
using namespace std;

Pool::Pool(size_t elemSize, size_t blockSize, bool traceFlag)
   : elemSize(elemSize), blockSize(blockSize)
{
   assert(elemSize >= sizeof(char*));
   if (traceFlag)
      cout << "Initializing a pool with element size " << elemSize << " and block size " << blockSize << endl;
   this->traceFlag = traceFlag;
   nBlocks = 0;
   free = 0;
   pool = 0;   // using lazy evaluation
}

Pool::~Pool()
{
   if (traceFlag)
      cout << "Deleting " << nBlocks << " blocks\n";
   for (size_t i = 0; i < nBlocks; ++i)
      delete [] pool[i];
   delete [] pool;
}

void Pool::link(char* base, size_t ncells, size_t elemSize, bool traceFlag)
{
   if (traceFlag)
      cout << "Linking cells starting at " << static_cast<void*>(base) << endl;
   for (size_t i = 0; i < ncells-1; ++i)
   {
      char* where = base + i*elemSize;
      storePtr(where, where + elemSize);
   }
   char* last = base + elemSize*(ncells-1);
   storePtr(last, 0);
}

void Pool::expand()
{
   if (traceFlag)
      cout << "Expanding pool...\n";
   
   // Expand pool by 1 block (row)
   // This code is safe for the initial allocation
   // (because nBlocks == 0 in that case)
   char** newPool = new char*[nBlocks+1];
   for (size_t i = 0; i < nBlocks; ++i)
      newPool[i] = pool[i];
   delete [] pool;
   pool = newPool;

   // Allocate/initialize the new block
   pool[nBlocks] = new char[blockSize * elemSize];
   link(pool[nBlocks], blockSize, elemSize, traceFlag);
   free = pool[nBlocks++];
}

void* Pool::allocate()
{
   if (free == 0)
      expand();

   assert(free != 0);   // Invariant
   void* loc = free;
   free = getPtr(static_cast<char*>(free));
   if (traceFlag)
      cout << "Cell allocated at " << loc << endl;
   return loc;
}

void Pool::deallocate(void* p)
{
   // Prepend p to the free list
   char* where = static_cast<char*>(p);
   storePtr(where, static_cast<char*>(free));
   free = where;
   if (traceFlag)
      cout << "Cell deallocated at " << p << endl;
}

